package Master.interfaces;

import java.awt.Color;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.text.DecimalFormat;
import java.util.ArrayList;

import javax.swing.BorderFactory;
import javax.swing.DefaultListModel;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JFileChooser;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextField;

import Master.*;

/*
 * Extended from abstract ClientGUI to get predetermined things and functions
 * for displaying GUI frames
 */
public class InputScreen extends ClientGUI {
	/*
	 * GUI components
	 */
	private JFileChooser resource_chooser = new JFileChooser();
	private JFileChooser taskinfo_chooser = new JFileChooser();
	private DisplayScrollPanel resource_slider=new DisplayScrollPanel();
	private DisplayScrollPanel task_slider=new DisplayScrollPanel();
	private final DefaultListModel model = new DefaultListModel();
	private JList taskplan_li = new JList(model);
	private JScrollPane taskplan_slider = new JScrollPane(taskplan_li);
	private JTextField resources_tf = new JTextField();
	private JTextField taskinfo_tf = new JTextField();
	private JTextField taskplaninfo_tf = new JTextField();
	private JButton browseRes_btn = new JButton("Load resource XML",
			new ImageIcon("client_images/gui/resource.gif"));
	private JButton browseTask_btn = new JButton("Load task XML",
			new ImageIcon("client_images/gui/task.gif"));
	private JButton updateTask_btn = 
		new JButton("Update task budget, deadline and priority");
	private JLabel resource_text = new JLabel("Resources");
	private JLabel taskplan_text = new JLabel("TaskPlans");
	private JLabel task_text = new JLabel("Tasks");
	private JLabel title_text = new JLabel("Job Management System");
	private JButton execute_btn = 
		new JButton("Execute",new ImageIcon("client_images/gui/go.gif"));
	private JButton exit_btn = 
		new JButton("Exit",new ImageIcon("client_images/gui/exit.gif"));
	private JTextField budget_tf = new JTextField();
	private JTextField deadline_tf = new JTextField();
	private JLabel budget_text = new JLabel("Budget (Dollars)");
	private JLabel deadline_text = new JLabel("Deadline (Seconds)");
	private JLabel strategy_text = new JLabel("Strategy");
	private JComboBox strategy_cbox = new JComboBox();
	
	/*
	 * Variables
	 */
	private ArrayList<Resource> resources = null;
	private ArrayList<Task> tasks = null;
	private ArrayList<TaskPlan> taskPlans = null;
	private PriorityQueue priorityQueue = null;
	private int taskPlanID = 0;
	private TaskPlan taskPlan = null;

	public InputScreen() {
		super();
		
		title_text.setFont(new Font(null,Font.BOLD,20));
		resource_text.setFont(new Font(null,Font.BOLD,15));
		taskplan_text.setFont(new Font(null,Font.BOLD,15));
		task_text.setFont(new Font(null,Font.BOLD,15));
		
		resources_tf.setPreferredSize(new Dimension(260,30));
		resources_tf.setEditable(false);
		resources_tf.setBackground(Color.WHITE);
		
		taskinfo_tf.setPreferredSize(new Dimension(260,30));
		taskinfo_tf.setEditable(false);
		taskinfo_tf.setBackground(Color.WHITE);
		
		taskplaninfo_tf.setPreferredSize(new Dimension(260,30));
		taskplaninfo_tf.setEditable(true);
		taskplaninfo_tf.setBackground(Color.WHITE);
		taskplaninfo_tf.setText(Message.updateInfo);
		
		browseRes_btn.setPreferredSize(new Dimension(260,30));
		updateTask_btn.setPreferredSize(new Dimension(260,30));
		browseTask_btn.setPreferredSize(new Dimension(260,30));
		
		budget_tf.setPreferredSize(new Dimension(200,30));
		deadline_tf.setPreferredSize(new Dimension(200,30));
		
		strategy_cbox.addItem(new ListItem(MasterEngine.AlgorithmFlag.
				FCFS.ordinal(), Message.fcfsInfo));
		strategy_cbox.addItem(new ListItem(MasterEngine.AlgorithmFlag.
				COST.ordinal(), Message.costInfo));
		strategy_cbox.addItem(new ListItem(MasterEngine.AlgorithmFlag.
				TIME.ordinal(), Message.timeInfo));
		
		taskPlans = new ArrayList<TaskPlan>();
		priorityQueue = new PriorityQueue(taskPlans);
		
		this.setVisible(true);
	}

	public void display(Container screen_container) {
		GridBagConstraints c = new GridBagConstraints();
		c.fill = GridBagConstraints.HORIZONTAL;
		screen_container.setLayout(new FlowLayout());
		
		/*
		 * Title
		 */
		JPanel title_p = new JPanel();
		title_p.setLayout(new GridBagLayout());
		title_p.add(title_text);
		screen_container.add(title_p);
		
		/*
		 * Main panel
		 */
		JPanel main_p = new JPanel();
		main_p.setLayout(new GridBagLayout());
		screen_container.add(main_p);
		
		/*
		 * Resource panel
		 */
		JPanel resource_p = new JPanel();
		resource_p.setBorder(BorderFactory.createTitledBorder("Resources"));
		resource_p.setLayout(new GridBagLayout());
		c.gridx = 0;
		c.gridy = 0;
		main_p.add(resource_p,c);
		
		c.gridx = 0;
		c.gridy = 1;
		c.insets=new Insets(0,5,2,5);
		resource_p.add(resource_slider,c);
		resource_slider.setPreferredSize(new Dimension(400,200));
		c.ipady=1;
		c.ipadx=1;
		
		c.gridx = 0;
		c.gridy = 2;
		resource_p.add(resources_tf,c);
		
		c.gridx = 0;
		c.gridy = 3;
		resource_p.add(browseRes_btn,c);
		c.insets=new Insets(0,0,0,0);
		
		/*
		 * Task Plan panel
		 */
		JPanel taskplan_p = new JPanel();
		taskplan_p.setBorder(
			BorderFactory.createTitledBorder("Priority Queue and Scheduling"));
		taskplan_p.setLayout(new GridBagLayout());
		c.gridx = 0;
		c.gridy = 1;
		c.insets=new Insets(10,0,0,0);
		main_p.add(taskplan_p,c);
		
		c.gridx = 0;
		c.gridy = 1;
		c.insets=new Insets(0,5,2,5);
		taskplan_p.add(taskplan_slider,c);
		taskplan_slider.setPreferredSize(new Dimension(400,100));
		
		c.gridx = 0;
		c.gridy = 2;
		taskplan_p.add(taskplaninfo_tf,c);
		
		c.gridx = 0;
		c.gridy = 3;
		taskplan_p.add(updateTask_btn,c);
		
		/*
		 * Actions in Task Plan panel
		 */
		c.gridx = 0;
		c.gridy = 4;
		c.insets=new Insets(20,5,0,5);
		taskplan_p.add(execute_btn,c);
		
		c.insets=new Insets(0,5,0,5);
		c.gridx = 0;
		c.gridy = 5;
		taskplan_p.add(exit_btn,c);
		c.insets=new Insets(0,0,0,0);
		
		/*
		 * Task panel
		 */
		JPanel task_p = new JPanel();
		task_p.setBorder(BorderFactory.createTitledBorder("New Task Plan"));
		task_p.setLayout(new GridBagLayout());
		c.gridx = 2;
		c.gridy = 0;
		c.gridheight=2;
		c.insets=new Insets(0,50,0,0);
		main_p.add(task_p,c);
		c.gridheight=1;
		
		/*
		 * Inputs in Task panel
		 */
		JPanel input_info_p = new JPanel();
		input_info_p.setBorder(
				BorderFactory.createTitledBorder("Execution inputs"));
		input_info_p.setLayout(new GridBagLayout());
		c.gridx = 0;
		c.gridy = 0;
		c.insets=new Insets(20,20,20,20);
		task_p.add(input_info_p,c);
		
		c.gridx = 0;
		c.gridy = 0;
		c.insets=new Insets(0,10,10,10);
		input_info_p.add(budget_text,c);
		
		c.gridx = 1;
		c.gridy = 0;
		input_info_p.add(budget_tf,c);
		
		c.gridx = 0;
		c.gridy = 1;
		input_info_p.add(deadline_text,c);
		
		c.gridx = 1;
		c.gridy = 1;
		input_info_p.add(deadline_tf,c);
		
		c.gridx = 0;
		c.gridy = 2;
		input_info_p.add(strategy_text,c);
		
		c.gridx = 1;
		c.gridy = 2;
		input_info_p.add(strategy_cbox,c);
		c.insets=new Insets(0,0,0,0);
		
		/*
		 * Task components in Task panel
		 */
		c.gridx = 0;
		c.gridy = 1;
		c.insets=new Insets(0,5,2,5);
		task_p.add(task_slider,c);
		task_slider.setPreferredSize(new Dimension(450,300));
		
		c.gridx = 0;
		c.gridy = 2;
		task_p.add(taskinfo_tf,c);
		
		c.gridx = 0;
		c.gridy = 3;
		task_p.add(browseTask_btn,c);
		
	}

	protected void initialise_listeners() {
		final InputScreen referScreen=this;
		
		exit_btn.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent arg0) {
				perform_exit_operation();
			}
		});
		
		browseRes_btn.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent arg0) {
				resource_chooser.showOpenDialog(referScreen);
				if(resource_chooser.getSelectedFile()==null)
					return;
				
				String filepath=
					resource_chooser.getSelectedFile().getAbsolutePath();
				if(validateXMLFile(filepath))
				{
					resources_tf.setText(filepath);
					display_resources(filepath);
				}
				else
					resources_tf.setText("");
			}
		});
		
		browseTask_btn.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent arg0) {
				float budget = getFloatFromStr(budget_tf.getText(), 
						Message.budgetError);
				float deadline = getFloatFromStr(deadline_tf.getText(), 
						Message.deadlineError);
				if(budget == 0 || deadline == 0){
					budget_tf.setText("");
					deadline_tf.setText("");
					JOptionPane.showMessageDialog(null,Message.emptyError,
							"Error",JOptionPane.ERROR_MESSAGE);
					return;
				}
				
				int openFlag = taskinfo_chooser.showOpenDialog(referScreen);
				if(openFlag == JFileChooser.CANCEL_OPTION) return;
				if(taskinfo_chooser.getSelectedFile() == null) return;
				
				String filepath = 
					taskinfo_chooser.getSelectedFile().getAbsolutePath();
				if(validateXMLFile(filepath))
				{
					taskPlanID++;
					taskinfo_tf.setText(filepath);
					display_tasks(filepath);
					
					addTaskPlan(filepath);
					showTaskPlan();
				}
				else
					taskinfo_tf.setText("");
			}
		});
		
		taskplaninfo_tf.addMouseListener(new java.awt.event.MouseAdapter() {   
			public void mouseExited(java.awt.event.MouseEvent e) {    
				if(taskplaninfo_tf.getText().length() == 0)
					taskplaninfo_tf.setText(Message.updateInfo);
				taskplaninfo_tf.setEditable(false);
			}
			public void mouseEntered(java.awt.event.MouseEvent e) {
				if(taskplaninfo_tf.getText().equals(Message.updateInfo))
					taskplaninfo_tf.setText("");
				taskplaninfo_tf.setEditable(true);
			}
		});
		
		updateTask_btn.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent arg0) {
				String selectedRow = (String) taskplan_li.getSelectedValue();

				if(selectedRow == null){
					JOptionPane.showMessageDialog(null,Message.selectError,
							"Error",JOptionPane.ERROR_MESSAGE);
					return;
				}
				String curTaskPlanID_input = selectedRow.trim().split(" ")[0];

				String budget_input = budget_tf.getText().trim();
				String deadline_input = deadline_tf.getText().trim();
				String priority_input =  taskplaninfo_tf.getText().trim();
				
				/*int curTaskPlanID = getIntFromStr(curTaskPlanID_input, 
				 * Message.taskPlanIDError);
				if(curTaskPlanID == 0){
					taskplaninfo_tf.setText("");
					return;
				}*/
				
				int curTaskPlanID = getIntFromStr(curTaskPlanID_input, null);
				float budget = 
					getFloatFromStr(budget_input, Message.budgetError);
				float deadline = 
					getFloatFromStr(deadline_input, Message.deadlineError);
				int priority = 
					getIntFromStr(priority_input, Message.priorityError);
					
				updateTaskPlan(curTaskPlanID, budget, deadline, priority);
				showTaskPlan();
			}
		});
		
		execute_btn.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent arg0) {
					execution_procedure();
			}
		});
		
		strategy_cbox.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent arg0) {
				System.out.println(strategy_cbox.getSelectedIndex());
				MasterEngine.algorithmFlag = 
					MasterEngine.AlgorithmFlag.values()[strategy_cbox.getSelectedIndex()];
			}
		});
		
		taskplan_li.addMouseListener(new MouseAdapter(){
			public void mouseClicked(MouseEvent e) {
				if (e.getClickCount() == 2) {
		             int curIndex = taskplan_li.locationToIndex(e.getPoint());
		             if(curIndex == 0) return;
		             taskinfo_tf.setText(taskPlans.get(curIndex).getFilepath());
		             show_tasks(taskPlans.get(curIndex).getTasks());
		          }
			}
		}
		);
	}

	private void addTaskPlan(String filepath){
		TaskPlan taskPlan = create_taskplan(filepath);

		if(taskPlan == null) return;
		priorityQueue.insertATaskPlan(taskPlan);
		taskPlans = priorityQueue.getTaskPlans();
	}
	
	private void updateTaskPlan(int curTaskID, float budget, float deadline, int priority){
		TaskPlan tmpTaskPlan = null, updatedTaskPlan = null;

		for(int i = 1; i < taskPlans.size(); i++){
			tmpTaskPlan = taskPlans.get(i);
			if(tmpTaskPlan != null && tmpTaskPlan.getTaskPlanID() == curTaskID){
				updatedTaskPlan = taskPlans.remove(i);
				break;
			}
		}
		if(updatedTaskPlan == null){
			JOptionPane.showMessageDialog(null,Message.idNotFound,
					"Info",JOptionPane.INFORMATION_MESSAGE);
			return;
		}
		priorityQueue = new PriorityQueue(taskPlans);
		updatedTaskPlan.setBudget(budget);
		updatedTaskPlan.setTimeRequired(deadline);
		updatedTaskPlan.setPriority(priority);
		priorityQueue.insertATaskPlan(updatedTaskPlan);
		taskPlans = priorityQueue.getTaskPlans();
	}
	
	private void showTaskPlan(){
		String allStr, status = "waiting", title;
		TaskPlan taskPlan = null;
		DecimalFormat format1 = new DecimalFormat("000");
		DecimalFormat format2 = new DecimalFormat("000.00");
		model.clear();
		
		/*
		 * #####TaskPlanID######Budget######DeadLine########Status######Priority
		 * ######001############000.00#######000.00#########waiting#######001
		 */
		title = String.format("%15s%12s%14s%14s%18s","TaskPlanID","Budget",
				"DeadLine","Status","Priority");
		model.addElement(title);
		for(int i = 1; i < taskPlans.size(); i++){
			taskPlan = taskPlans.get(i);
			allStr = String.format("%15s%20s%14s%18s%16s", 
					format1.format(taskPlan.getTaskPlanID()), 
					format2.format(taskPlan.getBudget()), 
					format2.format(taskPlan.getTimeRequired()), status,
					format1.format(taskPlan.getPriority()));
			model.addElement(allStr);
		}
	}
	
	private boolean validateXMLFile(String filepath)
	{
		String[] ss = filepath.split("\\.");
		if(ss.length>1)
		{
			String extension = ss[1];
			if(extension.equalsIgnoreCase("xml"))
				return true;
			else
			{
				JOptionPane.showMessageDialog(null,
						"The selected file is not XML.","Error",
						JOptionPane.ERROR_MESSAGE);
				return false;
			}
		}

		return false;
	}
	
	public void execution_procedure()
	{
        System.out.println("execution_procedure");
        
        taskPlan = priorityQueue.deleteATaskPlan();

        taskPlans = priorityQueue.getTaskPlans();
		
		if(taskPlan==null)//Due to input errors
			return;

        for(Resource resource : taskPlan.getResources()){
            resource.clearNextCompleteTime();
            resource.clearQueue();
            System.out.println("resource: " + resource.getHostName());
        }

        for(Task task : taskPlan.getTasks()){
            System.out.println("task: " + task.getTaskID());
        }

        boolean isFeasible = this.masterEngineRef().execute(taskPlan);
        if(!isFeasible){
        	resetTaskInputs();
        	return;
        }
        
		WorkerMonitor workMonitor = new WorkerMonitor(taskPlan,this);
		Thread thread = new Thread(workMonitor);
		thread.start();
        
        //Reset GUI components
        resetTaskInputs();
		execute_btn.setEnabled(false);//Disable this as well
	}
    
    private void resetTaskInputs()
    {
        task_slider.removeAllItems();
        budget_tf.setText("");
        deadline_tf.setText("");
        taskinfo_tf.setText("");
        showTaskPlan();
    }
	
	private int getIntFromStr(String intInput, String errorMsg){
		int intNum = 0;
		if(!intInput.equalsIgnoreCase(""))
		{
			try
			{
				intNum = Integer.parseInt(intInput);
				return intNum;
			}catch(Exception e){
				JOptionPane.showMessageDialog(null,errorMsg,
						"Error",JOptionPane.ERROR_MESSAGE);
			}
		}
		return 0;
	}
	
	private float getFloatFromStr(String floatInput, String errorMsg){
		float floatNum = 0;
		if(!floatInput.equalsIgnoreCase(""))
		{
			try
			{
				floatNum = Float.parseFloat(floatInput);
				return floatNum;
			}catch(Exception e){
				JOptionPane.showMessageDialog(null,errorMsg,
						"Error",JOptionPane.ERROR_MESSAGE);
			}
		}
		return 0;
	}
	
	private TaskPlan create_taskplan(String filepath)
	{
		//Default values
		float budget=0.0f;
		float deadline=0.0f;
		//int strategy=1;//??
		
		String budget_input=budget_tf.getText().trim();
		budget = getFloatFromStr(budget_input, Message.budgetError);
		
		/*if(!budget_input.equalsIgnoreCase(""))
		{
			try
			{
				budget=Float.parseFloat(budget_input);
			}catch(Exception e){
				JOptionPane.showMessageDialog(null,
				"Please enter numeric number for budget.",
						"Error",JOptionPane.ERROR_MESSAGE);
				return null;
			}
		}*/
		
		String deadline_input=deadline_tf.getText().trim();
		deadline = getFloatFromStr(deadline_input, Message.deadlineError);
		
		/*if(!deadline_input.equalsIgnoreCase(""))
		{
			try
			{
				deadline=Float.parseFloat(deadline_input);
			}catch(Exception e){
				JOptionPane.showMessageDialog(null,
				"Please enter HH.MM for deadline.",
						"Error",JOptionPane.ERROR_MESSAGE);
				return null;
			}
		}*/
		
		//strategy=((ListItem)strategy_cbox.getSelectedItem()).getID();
		
		System.out.println("Budget: "+budget);
		System.out.println("Deadline: "+deadline);
		//System.out.println("Strategy: "+strategy);
		TaskPlan taskPlan=new TaskPlan(taskPlanID, budget,deadline, filepath);
		
		taskPlan=attach(taskPlan,resources,tasks);

		return taskPlan;
	}
	
	private void display_resources(String filepath)
	{
		try
		{
			int y=0;
			resources = IO.readResourceXML(filepath);
			
			if(resources.size()<=0)
				throw new Exception();//lazy
			
			for(Resource this_resource:resources)
				resource_slider.add(0, y++, new ResourcePanelItem(this_resource));
		}catch(Exception e)
		{
			e.printStackTrace();
			JOptionPane.showMessageDialog(null,
					"Error: The resource XML file is misformated.",
					"Error",JOptionPane.ERROR_MESSAGE);
			resources_tf.setText("");
			resources=null;
			resource_slider.removeAllItems();
		}
	}
	
	private void show_tasks(ArrayList<Task> tasks){
		task_slider.removeAllItems();
		int y=0;
		for(Task this_task:tasks)
			task_slider.add(0,y++,new TaskPanelItem(this_task));
	}
	
	private void display_tasks(String filepath)
	{
		try
		{
			
			tasks = IO.readTaskXML(filepath);
			
			if(tasks.size()<=0)
				throw new Exception();//lazy
			
			//show all the tasks of this taskPlan to the screen
			show_tasks(tasks);
			/*int y=0;
			for(Task this_task:tasks)
				task_slider.add(0,y++,new TaskPanelItem(this_task));*/
		}catch(Exception e)
		{
			e.printStackTrace();
			JOptionPane.showMessageDialog(null,
					"Error: The task XML file is misformated.",
					"Error",JOptionPane.ERROR_MESSAGE);
			taskinfo_tf.setText("");
			tasks=null;
			task_slider.removeAllItems();
		}
	}
	
	private TaskPlan attach(TaskPlan this_plan, 
			ArrayList<Resource> this_resources, 
			ArrayList<Task> this_tasks)
	{
		if(this_resources==null || this_tasks==null)
		{
			JOptionPane.showMessageDialog(null,
					"Please upload your resource or task XML file.",
					"Error",JOptionPane.ERROR_MESSAGE);
			return null;
		}
		
		for(Resource r:this_resources){
            if(RegistryInfo.AuthSecutiry(r)) {
			    this_plan.addResource(r);
            }
        }
		
		for(Task t:this_tasks) {
			this_plan.addTask(t);
        }
		
		return this_plan;
	}
	
	/*
	 * Little help class for lists
	 */
	private class ListItem {
		private int id;
		private String public_text;

		public ListItem(int id,String public_text){
			this.id=id;
			this.public_text=public_text;
		}
		public String toString(){return public_text;}
		public int getID(){return id;}
	}

	public ArrayList<Resource> getResources() {
		return resources;
	}

	public ArrayList<Task> getTasks() {
		return tasks;
	}

	public PriorityQueue getPriorityQueue() {
		return priorityQueue;
	}
	
	public ClientGUI getClientScreen()
	{
		return this;
	}

	public JButton getExecute_btn() {
		return execute_btn;
	}

	public ArrayList<TaskPlan> getTaskPlans() {
		return taskPlans;
	}

	public TaskPlan getTaskplan() {
		return taskPlan;
	}
}
